/**
 * Copyright (c) 2018 qar All rights reserved.
 *
 * https://www.qar.io
 *
 * 版权所有，侵权必究！
 */

package com.example.mybatis.interceptor;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
//import com.example.commons.mybatis.entity.DataScope;
import com.example.mybatis.entity.DataScope;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数据过滤
 *
 * @author Mark sunlightcs@gmail.com
 */
public class DataFilterInterceptor implements InnerInterceptor {

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        DataScope scope = getDataScope(parameter);
        // 不进行数据过滤
        if(scope == null){
            return;
        }

        // 拼接新SQL
        String buildSql = getSelect(boundSql.getSql(), scope);

        // 重写SQL
        PluginUtils.mpBoundSql(boundSql).sql(buildSql);
    }

    private DataScope getDataScope(Object parameter){
        if (parameter == null){
            return null;
        }

        // 判断参数里是否有DataScope对象
        if (parameter instanceof Map) {
            Map<?, ?> parameterMap = (Map<?, ?>) parameter;
            for (Map.Entry entry : parameterMap.entrySet()) {
                if (entry.getValue() != null && entry.getValue() instanceof DataScope) {
                    return (DataScope) entry.getValue();
                }
            }
        } else if (parameter instanceof DataScope) {
            return (DataScope) parameter;
        }

        return null;
    }

    private String getSelect(String buildSql, DataScope scope){
        try {
            //将数据权限过滤的SQL构建好
            Table table = null;
            if(StrUtil.isNotEmpty(scope.getTableAlias())) {
                table = new Table(scope.getTableAlias());
            }

            InExpression dataScopeExpression = null;
            if(StrUtil.isNotEmpty(scope.getDataScopeColumn())) {
                ItemsList itemsList = new ExpressionList(scope.getDataScopeList().stream().map(StringValue::new).collect(Collectors.toList())); // 把集合转变为JSQLParser需要的元素列表
                dataScopeExpression = new InExpression(new Column(table,scope.getDataScopeColumn()), itemsList); // 创建IN表达式对象，传入列名及IN范围列
            }

            //用户ID过滤
            EqualsTo userEq = null;
            if(StrUtil.isNotEmpty(scope.getUserIdColumn())) {
                userEq = new EqualsTo(new Column(table,scope.getUserIdColumn()),new LongValue(scope.getUserId()));
            }

            Select select = (Select) CCJSqlParserUtil.parse(buildSql);
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();

            Expression expression = plainSelect.getWhere();
            if(expression == null){
                if(dataScopeExpression != null && userEq != null) {
                    AndExpression andExpression = new AndExpression();
                    andExpression.setLeftExpression(dataScopeExpression);
                    andExpression.setRightExpression(userEq);
                    plainSelect.setWhere(andExpression);
                } else if(dataScopeExpression != null) {
                    plainSelect.setWhere(dataScopeExpression);
                } else if(userEq != null) {
                    plainSelect.setWhere(userEq);
                }
            }else{
                if(dataScopeExpression != null && userEq != null) {
                    AndExpression andExpression = new AndExpression(expression, dataScopeExpression);
                    AndExpression nextExpression = new AndExpression(andExpression, userEq);
                    plainSelect.setWhere(nextExpression);
                } else if(dataScopeExpression != null) {
                    AndExpression andExpression = new AndExpression(expression, dataScopeExpression);
                    plainSelect.setWhere(andExpression);
                } else if(userEq != null) {
                    AndExpression andExpression = new AndExpression(expression, userEq);
                    plainSelect.setWhere(andExpression);
                }
            }
            return select.toString();
        }catch (JSQLParserException e){
            return buildSql;
        }
    }
}